#include "stdafx.h"
#include "WM_InputWrapper.h"
#include "DL_Debug.h"
#include "ExtendedString.h"
#include "debug.h"

WMInput::WM_InputWrapper* WMInput::WM_InputWrapper::ourInstance = NULL;

void WMInput::WM_InputWrapper::Create(const Settings& aSettings)
{
	if(aSettings.appWindow == NULL)
	{
		DL_ASSERT("Error initializing input. No hWnd supplied.");
		return;
	}
	ourInstance = new WM_InputWrapper(aSettings);
}

void WMInput::WM_InputWrapper::Destroy()
{
	delete ourInstance;
	ourInstance = NULL;
}

WMInput::WM_InputWrapper* WMInput::WM_InputWrapper::GetInstance()
{
	return ourInstance;
}

bool WMInput::WM_InputWrapper::KeyState(Keys aKey) const
{
	return myKeysDown[aKey];
}

bool WMInput::WM_InputWrapper::KeyPressed(Keys aKey) const
{
	if(myKeysPressed[aKey] == PRESSED)
		return true;

	return false;
}

bool WMInput::WM_InputWrapper::KeyReleased(Keys aKey) const
{
	if(myKeysPressed[aKey] == RELEASED)
		return true;

	return false;
}

float WMInput::WM_InputWrapper::GetMouseWheelMovement() const
{
	return myMouseWheelMovement;
}

void WMInput::WM_InputWrapper::SetMousePosition( const int aX, const int aY )
{
	//SetCursorPos( aX+myClientRectangle.left, aY+myClientRectangle.top );
}

void WMInput::WM_InputWrapper::GetMouseDeltaPosition( float& aX, float& aY )
{
	aX = myMouseDelta.x;
	aY = myMouseDelta.y;
}

void WMInput::WM_InputWrapper::Update()
{
	ZeroMemory(myKeysPressed, sizeof(myKeysPressed));
	myMouseDelta.Set(0,0);
	myMouseWheelMovement = 0;

	if(mySettings.useRawInput == false)
	{
		POINT newMousePos;
		GetCursorPos(&newMousePos);
		myMouseDelta.x = static_cast<float>(newMousePos.x - myPreviousMousePos.x);
		myMouseDelta.y = static_cast<float>(newMousePos.y - myPreviousMousePos.y);
		myPreviousMousePos = newMousePos;
	}

	if(mySettings.showCursor == false && myHasFocus)
	{
		CenterCursor();
		GetCursorPos(&myPreviousMousePos);
	}
}

void WMInput::WM_InputWrapper::HandleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
	case WM_KEYDOWN:
		{
			if(myKeysDown[static_cast<int>(wParam)] != PRESSED) // To stop the windows delay repeat
			{
				myKeysDown[static_cast<int>(wParam)] = true;
				myKeysPressed[static_cast<int>(wParam)] = PRESSED;
			}
		}
		return;

	case WM_KEYUP:
		{
			int key = static_cast<int>(wParam);
			if (key != -1)
			{
				myKeysDown[key] = false;
				myKeysPressed[key] = RELEASED;
				break;
			}
		}
		return;

	case WM_KILLFOCUS:
		{
			myHasFocus = false;
			ClipCursor(NULL);
			ShowCursor(true);
		}
		return;

	case WM_SETFOCUS:
		{
			myHasFocus = true;
			if(mySettings.showCursor == false)
			{
				ShowCursor(false);
				ClipCursor(&myWindowRect);
				CenterCursor();
				GetCursorPos(&myPreviousMousePos);
			}
			else if(mySettings.clipCursor == true)
			{
				ClipCursor(&myWindowRect);
			}
		}
		return;

	case WM_EXITSIZEMOVE:
		GetWindowRect(mySettings.appWindow, &myWindowRect);
		if(mySettings.showCursor == false || mySettings.clipCursor == true)
		{
			ClipCursor(&myWindowRect);
		}
		return;
	}

	if(mySettings.useRawInput)
	{
		HandleRawInput(hWnd, message, wParam, lParam);
	}
	else
	{
		HandleNormalInput(hWnd, message, wParam, lParam);
	}
}

void WMInput::WM_InputWrapper::HandleRawInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if(message == WM_INPUT)
	{
		UINT dwSize;

		GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, 
			sizeof(RAWINPUTHEADER));
		LPBYTE lpb = new BYTE[dwSize];
		if (lpb == NULL) 
		{
			DL_ERROR("Error creating input recieveing buffer.");
			return;
		} 

		if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, 
			sizeof(RAWINPUTHEADER)) != dwSize )
		{
			DL_ERROR("GetRawInputData does not return correct size !");
			return;
		}

		RAWINPUT* raw = reinterpret_cast<RAWINPUT*>(lpb);

		switch(raw->header.dwType)
		{
		case RIM_TYPEMOUSE:
			myMouseDelta.x += static_cast<float>(raw->data.mouse.lLastX);
			myMouseDelta.y += static_cast<float>(raw->data.mouse.lLastY);

			if(raw->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
				HandleMousePress(MK_LBUTTON, true);

			if(raw->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP)
				HandleMousePress(MK_LBUTTON, false);

			if(raw->data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN)
				HandleMousePress(MK_MBUTTON, true);

			if(raw->data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP)
				HandleMousePress(MK_MBUTTON, false);

			if(raw->data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN)
				HandleMousePress(MK_RBUTTON, true);

			if(raw->data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP)
				HandleMousePress(MK_RBUTTON, false);

			if(raw->data.mouse.usButtonFlags & RI_MOUSE_WHEEL)
				myMouseWheelMovement += static_cast<SHORT>(raw->data.mouse.usButtonData);

			break;
		}
	}
}

void WMInput::WM_InputWrapper::HandleNormalInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
	case WM_LBUTTONDOWN:
		HandleMousePress(MK_LBUTTON, 1);
		break;
	case WM_RBUTTONDOWN:
		HandleMousePress(MK_RBUTTON, 1);
		break;
	case WM_MBUTTONDOWN:
		HandleMousePress(MK_MBUTTON, 1);
		break;
	case WM_XBUTTONDOWN:
		HandleMousePress(MK_XBUTTON1, 1);
		break;

	case WM_LBUTTONUP:
		HandleMousePress(MK_LBUTTON, 0);
		break;
	case WM_RBUTTONUP:
		HandleMousePress(MK_RBUTTON, 0);
		break;
	case WM_MBUTTONUP:
		HandleMousePress(MK_MBUTTON, 0);
		break;
	case WM_XBUTTONUP:
		HandleMousePress(MK_XBUTTON1, 0);
		break;

// 	case WM_MOUSEMOVE:
// 		{
// 			POINTS newMousePos = MAKEPOINTS(lParam);
// 			myMouseDelta.x += static_cast<float>(newMousePos.x - myPreviousMousePos.x);
// 			myMouseDelta.y += static_cast<float>(newMousePos.y - myPreviousMousePos.y);
// 			myPreviousMousePos.x = newMousePos.x;
// 			myPreviousMousePos.y = newMousePos.y;
// 		}
	}

}

bool WMInput::WM_InputWrapper::HandleMousePress(WPARAM wParam, const bool aState)
{
	switch(wParam)
	{
	case MK_LBUTTON:
		myKeysDown[MOUSE_LBUTTON] = aState;
		if(aState == true)
			myKeysPressed[MOUSE_LBUTTON] = PRESSED;
		else
			myKeysPressed[MOUSE_LBUTTON] = RELEASED;
		return true;

	case MK_RBUTTON:
		myKeysDown[MOUSE_RBUTTON] = aState;
		if(aState == true)
			myKeysPressed[MOUSE_RBUTTON] = PRESSED;
		else
			myKeysPressed[MOUSE_RBUTTON] = RELEASED;
		return true;

	case MK_MBUTTON:
		myKeysDown[MOUSE_MBUTTON] = aState;
		if(aState == true)
			myKeysPressed[MOUSE_MBUTTON] = PRESSED;
		else
			myKeysPressed[MOUSE_MBUTTON] = RELEASED;
		return true;

	case MK_XBUTTON1:
		myKeysDown[MOUSE_XBUTTON1] = aState;
		if(aState == true)
			myKeysPressed[MOUSE_XBUTTON1] = PRESSED;
		else
			myKeysPressed[MOUSE_XBUTTON1] = RELEASED;
		return true;

	case MK_XBUTTON2:
		myKeysDown[MOUSE_XBUTTON2] = aState;
		if(aState == true)
			myKeysPressed[MOUSE_XBUTTON2] = PRESSED;
		else
			myKeysPressed[MOUSE_XBUTTON2] = RELEASED;
		return true;
	}

	return false;
}

void WMInput::WM_InputWrapper::CenterCursor()
{
	Vector2i center;
	center.x = (myWindowRect.right - myWindowRect.left) / 2 + myWindowRect.left;
	center.y = (myWindowRect.bottom - myWindowRect.top) / 2 + myWindowRect.top;
	SetCursorPos(center.x, center.y);
}

WMInput::WM_InputWrapper::WM_InputWrapper(const Settings& aSettings)
{
	myMouseDelta.Set(0,0);
	myMouseWheelMovement = 0;

	mySettings = aSettings;

	ZeroMemory(myKeysDown, sizeof(myKeysDown));
	ZeroMemory(myKeysPressed, sizeof(myKeysPressed));
	
	GetWindowRect(mySettings.appWindow, &myWindowRect);
	CenterCursor();
	GetCursorPos(&myPreviousMousePos);
	ShowCursor(aSettings.showCursor);
	
	if(mySettings.showCursor == false || mySettings.clipCursor == true)
	{
		ClipCursor(&myWindowRect);
	}

	if(mySettings.useRawInput)
	{
		RAWINPUTDEVICE device;
		device.hwndTarget = mySettings.appWindow;
		device.usUsagePage = 0x01;
		device.usUsage = 0x02;
		device.dwFlags = 0; // Changed from RIDEV_NOLEGACY to enable titlebar

		if(!RegisterRawInputDevices(&device, 1, sizeof(RAWINPUTDEVICE)))
		{
			DWORD error = GetLastError();
			DL_ERROR(ExtendedString("Error capturing mouse, code: ") << error);
		}
	}

	myHasFocus = true;

}

WMInput::WM_InputWrapper::~WM_InputWrapper()
{
	ClipCursor(NULL);
}